import os
import uuid
from datetime import datetime

from django.db import transaction
from django.forms import model_to_dict
from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.response import Response
from rest_framework_simplejwt.authentication import JWTAuthentication

from apps.core.pagination import paginated_queryset_response
from apps.document.filters import DocumentAndFormsFilter
from apps.document.models import DocumentAndForms, OtherDocuments, AppointmentNotes, AppointmentNotesFiles
from apps.users.permissions import IsProvider, IsClient
from .models import ClientDocuments
from apps.client.models import Client
from apps.core.utils.gcs_operations import delete_file
from ..booking.models import Appointment


@api_view(('GET', 'POST'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def document_api(request):
    if request.method == 'POST':
        data = request.data
        DocumentAndForms.objects.create(**data)

        return Response({"msg": "Document has been created"}, status=status.HTTP_201_CREATED)

    if request.method == 'GET':
        ordering = request.GET.get('ordering', '-created_at')

        filtered_queryset = DocumentAndFormsFilter().filter_queryset(request, DocumentAndForms.objects.all(), None)

        documents = filtered_queryset.order_by(ordering).values(
            'id', 'title', 'type', 'is_checked', 'content'
        )

        documents = [dict(document) for document in documents]

        return paginated_queryset_response(queryset=documents, request=request)


@api_view(('GET', 'PATCH', 'DELETE'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def document_id_api(request, document_id):
    document = get_object_or_404(DocumentAndForms, id=document_id)
    if request.method == 'PATCH':
        data = request.data

        for field_name, field_value in data.items():
            if hasattr(DocumentAndFormsFilter, field_name):
                setattr(document, field_name, field_value)

        document.save()

        return Response({"msg": "Document has been updated"}, status=status.HTTP_200_OK)

    if request.method == 'GET':
        document = model_to_dict(document, exclude=['created_at', 'updated_at'])
        return Response(document, status=status.HTTP_200_OK)

    if request.method == 'DELETE':
        document.delete()
        return Response({"msg": "Document has been deleted"}, status=status.HTTP_204_NO_CONTENT)


from django_tenants.utils import schema_context


@api_view(('GET', 'POST'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def other_documents_api(request):
    with schema_context(request.user.provider.schema_name):
        if request.method == 'POST':
            files = request.FILES.getlist('document')
            request_data = []
            for file in files:
                name = file.name
                document_name = os.path.splitext(name)[0]
                data = {'document': file, 'title': name, 'document_name': document_name, 'document_id': uuid.uuid4()}
                request_data.append(OtherDocuments(**data))
            OtherDocuments.objects.bulk_create(request_data)

            return Response({"msg": "Document has been created"}, status=status.HTTP_201_CREATED)

        if request.method == 'GET':
            ordering = request.GET.get('ordering', '-created_at')

            documents = OtherDocuments.objects.all().order_by(ordering).values(
                'id', 'document', 'document_name', 'title'
            )

            documents = [dict(document) for document in documents]

            return paginated_queryset_response(queryset=documents, request=request)


@api_view(('GET', 'PATCH', 'DELETE'))
@authentication_classes([JWTAuthentication])
@permission_classes([IsProvider])
@transaction.atomic
def other_document_id_api(request, document_id):
    document = get_object_or_404(OtherDocuments, id=document_id)
    if request.method == 'PATCH':
        data = request.data
        document.document_name = data.get('document_name', document.document_name)
        document.save()
        return Response({"msg": "Other Document has been updated"}, status=status.HTTP_200_OK)

    if request.method == 'GET':
        document = model_to_dict(document, exclude=['created_at', 'updated_at'])
        return Response(document, status=status.HTTP_200_OK)

    if request.method == 'DELETE':
        document.delete()
        return Response({"msg": "Other Document has been deleted"}, status=status.HTTP_204_NO_CONTENT)


@api_view(['GET', 'POST'])
@permission_classes([IsProvider | IsClient])
def client_documents_crud(request):
    if request.method == 'GET':
        queryset = ClientDocuments.objects.all()
        user = request.user
        if user.is_client:
            queryset = queryset.filter(client=user)

        data = []
        for document in queryset:
            document_data = {
                'id': document.id,
                'client': document.client.id,
                'file': document.file.url if document.file else None,
                'content': document.content,
                'title': document.title,
                'created_at': str(int(document.created_at.timestamp())),
                'updated_at': str(int(document.updated_at.timestamp())) if document.updated_at else None
            }
            data.append(document_data)
        return paginated_queryset_response(data, request=request)
    if request.method == 'POST':
        get_client = Client.objects.filter(id=request.data.get('client')).first()
        if not get_client:
            return Response({'error': 'Client id is not provided'}, status=status.HTTP_400_BAD_REQUEST)

        files = request.FILES.getlist('file')
        documents = request.data.get('documents')
        if not files and not documents:
            return Response({'error': 'No files or documents provided'}, status=status.HTTP_400_BAD_REQUEST)

        data = []
        for file in files:
            data.append({
                'client': get_client,
                'file': file,
                'title': file.name,
            })

        # for document in documents:
        #     data.append({
        #         'client': client_id,
        #         'content': document.get('content'),
        #         'title': document.get('title'),
        #     })

        documents_created = []
        if data:
            for item in data:
                document = ClientDocuments.objects.create(**item)
                documents_created.append({
                    'id': document.id,
                    'client': str(get_client.id),
                    'file': document.file.url if document.file else None,
                    'content': document.content,
                    'title': document.title,
                    'created_at': str(int(document.created_at.timestamp())),
                    'updated_at': str(int(document.updated_at.timestamp())) if document.updated_at else None
                })
        else:
            client_documents = []
            for document in documents:
                client_document = ClientDocuments(
                                    client=get_client,
                                    title=document['title'],
                                    content=document['content']
                                )
                client_documents.append(client_document)
            if len(client_documents) > 0:
                ClientDocuments.objects.bulk_create(client_documents)
        return Response(documents_created, status=status.HTTP_201_CREATED)


@api_view(['GET', 'DELETE', 'PATCH'])
@permission_classes([IsProvider | IsClient])
def client_document_crud(request, pk):
    try:
        if request.method == 'GET':
            user = request.user
            queryset = ClientDocuments.objects.all()
            doc = None
            if user.is_client:
                doc = queryset.filter(client=user, id=pk).first()

            else:
                try:
                    doc = queryset.filter(id=pk).first()
                except ClientDocuments.DoesNotExist:
                    return Response({'error': 'Document not found'}, status=status.HTTP_404_NOT_FOUND)

            document_data = {}
            if doc:
                document_data = {
                    'id': doc.id,
                    'client': doc.client.id,
                    'file': doc.file.url if doc.file else None,
                    'content': doc.content,
                    'title': doc.title,
                    'created_at': doc.created_at.timestamp() if doc.created_at else None,
                    'updated_at': doc.updated_at.timestamp() if doc.updated_at else None
                }

            return Response(document_data)
        if request.method == 'DELETE':
            get_document = ClientDocuments.objects.filter(id=pk).first()
            if not get_document:
                return Response({
                    'success': False,
                    'message': 'Document does not exist'
                }, status=status.HTTP_400_BAD_REQUEST)

            if get_document.file:
                delete_document_file = delete_file(get_document.file.name)
                if delete_document_file[0]:
                    get_document.delete()
                    return Response({
                        'success': True,
                        'message': 'Client document deleted successfully'
                    }, status=status.HTTP_200_OK)
            else:
                get_document.delete()
                return Response({
                    'success': True,
                    'message': 'Client Document deleted successfully'
                }, status=status.HTTP_204_NO_CONTENT)
            # else:
            #     return Response({
            #         'success': False,
            #         'message': f'Failed to delete client document because of {delete_document_file[1]}'
            #     }, status=status.HTTP_200_OK)
        if request.method == 'PATCH':
            user = request.user
            try:
                document = ClientDocuments.objects.get(pk=pk)
            except ClientDocuments.DoesNotExist:
                return Response({'error': 'Document not found'}, status=status.HTTP_404_NOT_FOUND)

            if user.is_client and document.client != user.client:
                return Response({'error': 'You do not have permission to update this document'},
                                status=status.HTTP_403_FORBIDDEN)

            request_data = request.data.copy()
            file = request_data.pop('file', None)
            if file:
                document.file = file
            document.title = request_data.get('title', document.title)
            document.content = request_data.get('content', document.content)
            document.updated_at = datetime.now()
            document.save()

            document_data = {
                'id': document.id,
                'client': document.client.id,
                'file': document.file.url if document.file else None,
                'content': document.content,
                'title': document.title,
            }

            return Response(document_data)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to perform {request.method} operation in client_document_crud because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(['POST'])
@permission_classes([IsProvider | IsClient])
def create_client_document(request):
    client_id = request.data.get('client')
    if client_id is None:
        return Response({'error': 'Client id is not provided'}, status=status.HTTP_400_BAD_REQUEST)

    files = request.FILES.getlist('file')
    documents = request.data.get('documents')
    if not files and not documents:
        return Response({'error': 'No files or documents provided'}, status=status.HTTP_400_BAD_REQUEST)

    data = []
    for file in files:
        data.append({
            'client': client_id,
            'file': file,
            'title': file.name,
        })

    for document in documents:
        data.append({
            'client': client_id,
            'content': document.get('content'),
            'title': document.get('title'),
        })

    documents_created = []
    for item in data:
        document = ClientDocuments.objects.create(**item)
        documents_created.append({
            'id': document.id,
            'client': document.client.id,
            'file': document.file.url if document.file else None,
            'content': document.content,
            'title': document.title,
        })

    return Response(documents_created, status=status.HTTP_201_CREATED)


@api_view(['GET'])
@permission_classes([IsProvider | IsClient])
def retrieve_client_document(request, pk):
    if request.method == 'GET':
        user = request.user
        queryset = ClientDocuments.objects.all()
        document=None
        if user.is_client:
            document = queryset.filter(client=user)

        else:
            try:
                document = queryset.filter(client__id=pk)
            except ClientDocuments.DoesNotExist:
                return Response({'error': 'Document not found'}, status=status.HTTP_404_NOT_FOUND)

        document_data = {}
        response_data = []
        if document:
            for doc in document:
                response_data.append({
                    'id': doc.id,
                    'client': doc.client.id,
                    'file': doc.file.url if doc.file else None,
                    'content': doc.content,
                    'title': doc.title,
                    'created_at': doc.created_at.timestamp() if doc.created_at else None,
                    'updated_at': doc.updated_at.timestamp() if doc.updated_at else None
                })

        return paginated_queryset_response(response_data, request=request)


@api_view(['GET'])
@permission_classes([IsProvider])
def appointment_notes_list(request):
    queryset = AppointmentNotes.objects.all()
    user = request.user
    if user.is_provider:
        queryset = queryset.filter(appointment__provider=user)

    appointment_notes_list = []
    for note in queryset:
        notes_data = {
            'id': note.id,
            'appointment': note.appointment_id,
            'title': note.title,
            'content': note.content,
            'type': note.type,
            'date': note.date.timestamp(),
            'client': note.client_id,
            'time': note.time.timestamp(),
            'note': note.note,
            'appointment_notes_files': [{'id': file.id, 'title': file.title, 'file': file.file.url} for file in
                                        note.appointment_notes_files.all()]
        }
        appointment_notes_list.append(notes_data)

    return Response({'result': appointment_notes_list})


@api_view(['POST'])
@permission_classes([IsProvider])
def create_appointment_notes(request):
    try:
        if request.method == 'POST':
            request_data = request.data.copy()
            # get_client = Client.objects.filter(id=request_data['client']).first()
            # if not get_client:
            #     return Response({
            #         'success': False,
            #         'message': 'Client is required'
            #     }, status=status.HTTP_400_BAD_REQUEST)
            # request_data['client'] = get_client
            if 'date' in request_data:
                request_data['date'] = datetime.fromtimestamp(request_data['date'])
            if 'time' in request_data:
                request_data['time'] = datetime.strptime(request_data['time'], '%H:%M:%S')
            if 'appointment' in request_data:
                appointment = Appointment.objects.filter(id=request_data['appointment']).first()
                if not appointment:
                    return Response({
                        'success': False,
                        'message': 'Appointment is required'
                    }, status=status.HTTP_400_BAD_REQUEST)
                request_data['appointment'] = appointment
            if 'client' in request_data:
                client = Client.objects.filter(id=request_data['client']).first()
                if not client:
                    return Response({
                        'success': False,
                        'message': 'Client does not exist'
                    }, status=status.HTTP_400_BAD_REQUEST)
                request_data['client'] = client
            appointment_notes = AppointmentNotes.objects.create(**request_data)
            notes_data = {
                'id': appointment_notes.id,
                'appointment': appointment_notes.appointment_id,
                'title': appointment_notes.title,
                'content': appointment_notes.content,
                'type': appointment_notes.type,
                'date': str(int(appointment_notes.date.timestamp())) if appointment_notes.date else None,
                'client': appointment_notes.client.id if appointment_notes.client else None,
                'time': datetime.strftime(appointment_notes.time, '%H:%M:%S') if appointment_notes.time else None,
                'note': appointment_notes.note,
                'appointment_notes_files': [{'id': file.id, 'title': file.title, 'file': file.file.url} for file in
                                            appointment_notes.appointment_notes_files.all()]
            }
            return Response(notes_data, status=status.HTTP_201_CREATED)
        else:
            return Response({"detail": "Method 'POST' is required."}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to create_appointment_notes because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(['PATCH', 'DELETE'])
@permission_classes([IsProvider])
def delete_and_update_appointment_notes(request, pk):
    try:
        appointment_note = get_object_or_404(AppointmentNotes, id=pk)
        if request.method == 'PATCH':
            request_data = request.data.copy()
            appointment_note.content = request_data.get('content', appointment_note.content)
            appointment_note.save()
            updated_note_data = {
                'id': appointment_note.id,
                'appointment': appointment_note.appointment_id,
                'title': appointment_note.title,
                'content': appointment_note.content,
                'type': appointment_note.type,
                'date': str(int(appointment_note.date.timestamp())) if appointment_note.date else None,
                'client': appointment_note.client.id if appointment_note.client else None,
                'time': str(appointment_note.time),
                'note': appointment_note.note,
                'appointment_notes_files': [{'id': file.id, 'title': file.title, 'file': file.file.url} for file in
                                            appointment_note.appointment_notes_files.all()]
            }
            return Response(updated_note_data)
        if request.method == 'DELETE':
            appointment_note.delete()
            return Response({
                'success': True,
                'message': 'Appointment note delete successfully.'
            }, status=status.HTTP_200_OK)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to perform {request.method} action in delete_and_update_appointment_notes '
                       f'because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET'])
@permission_classes([IsProvider])
def delete_appointment_notes(request, pk):
    appointment_note = get_object_or_404(AppointmentNotes, id=pk)
    appointment_note.delete()
    return Response({"detail": "Deleted successfully."}, status=status.HTTP_204_NO_CONTENT)


@api_view(['POST'])
@permission_classes([IsProvider])
def create_appointment_notes_file(request, notes_pk):
    try:
        if notes_pk is None:
            return Response({
                    'success': False,
                    'message': 'Appointment Note is required'
                }, status=status.HTTP_400_BAD_REQUEST)
        appointment_note = AppointmentNotes.objects.filter(id=notes_pk).first()
        if appointment_note is None:
            return Response({
                'success': False,
                'message': 'Appointment Note does not exist'
            }, status=status.HTTP_400_BAD_REQUEST)
        files = request.FILES.getlist('files')
        request_data = []

        for file in files:
            data = {'appointment_notes': appointment_note, 'file': file, 'title': file.name}
            data=AppointmentNotesFiles(**data)
            request_data.append(data)

        notes_files = AppointmentNotesFiles.objects.bulk_create(request_data)
        response_data = []
        for note in notes_files:
            response_data.append({
                'id': note.id,
                'file': note.file.url if note.file else None,
                'title': note.title
            })

        return Response(response_data, status=status.HTTP_201_CREATED)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to create_appointment_notes because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)


@api_view(['DELETE'])
@permission_classes([IsProvider])
def delete_appointment_note(request, notes_pk=None, pk=None):
    try:
        if pk is None:
            return Response({
                'success': False,
                'message': 'Id is required'
            }, status=status.HTTP_400_BAD_REQUEST)
        file = AppointmentNotesFiles.objects.filter(id=pk).first()
        if file:
            file.delete()
            return Response({'msg': 'File has been deleted'}, status=status.HTTP_204_NO_CONTENT)
        else:
            return Response({'msg': 'File not found'}, status=status.HTTP_400_BAD_REQUEST)
    except Exception as e:
        return Response({
            'success': False,
            'message': f'Failed to create_appointment_notes because of {str(e)}'
        }, status=status.HTTP_400_BAD_REQUEST)